package uy.org.softtech.cssuite.entidad.usuario;

import static org.springframework.util.StringUtils.hasText;
import static org.springframework.util.StringUtils.trimWhitespace;
import static uy.org.softtech.cssuite.entidad.Entidad.USUARIO_NAMESPACE;
import static uy.org.softtech.cssuite.helpers.HelperFechaHora.fechaLarga;
import static uy.org.softtech.cssuite.helpers.HelperTextos.URUGUAY;
import static uy.org.softtech.cssuite.helpers.HelperTextos.formatearTelefono;

import java.io.Serializable;
import java.util.Date;
import java.util.Locale;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;

import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.annotate.JsonRootName;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.Index;

import uy.org.softtech.cssuite.entidad.Entidad;
import uy.org.softtech.cssuite.helpers.HelperFechaHora;
import uy.org.softtech.cssuite.helpers.HelperTextos;

/**
 * <p>Entidad que representa a un usuario. También mapea la declaración del tipo de dato XML 'usuario'.</p>
 * 
 * <p>El siguiente fragmento de esquema especifica el contenido esperado y representado por los atributos de esta clase.</p>
 *  
 * <pre>
 * &lt;xsd:complexType name="usuario">
 *  &lt;xsd:all>
 *   &lt;xsd:element name="id" type="xsd:ID"/>
 *   &lt;xsd:element name="apellido" type="{http://cssuite.softtech.org.uy/esquemas/tipos}nombre"/>
 *   &lt;xsd:element name="primerNombre" type="{http://cssuite.softtech.org.uy/esquemas/tipos}nombre"/>
 *   &lt;xsd:element name="usuario" type="{http://cssuite.softtech.org.uy/esquemas/tipos}correo"/>
 *   &lt;xsd:element name="bloqueado" type="xsd:boolean"/>
 *   &lt;xsd:element name="rol" type="{http://cssuite.softtech.org.uy/esquemas/entidades}rol"/>
 *   &lt;xsd:element name="segundoNombre" type="{http://cssuite.softtech.org.uy/esquemas/tipos}nombre" minOccurs="0" />
 *   &lt;xsd:element name="ultimoLogin" type="xsd:dateTime" minOccurs="0"/>
 *   &lt;xsd:element name="intentos" type="xsd:integer" minOccurs="0"/>
 *   &lt;xsd:element name="ultimoIntentoFallido" type="xsd:dateTime" minOccurs="0"/>
 *   &lt;xsd:element name="correoElectronico" type="{http://cssuite.softtech.org.uy/esquemas/tipos}correo" minOccurs="0"/>
 *   &lt;xsd:element name="telefono" type="{http://cssuite.softtech.org.uy/esquemas/tipos}texto" minOccurs="0"/>
 *   &lt;xsd:element name="celular" type="{http://cssuite.softtech.org.uy/esquemas/tipos}texto" minOccurs="0"/>
 *   &lt;xsd:element name="comentarios" type="{http://cssuite.softtech.org.uy/esquemas/tipos}comentario" minOccurs="0"/>
 *  &lt;/xsd:all>
 * &lt;/xsd:complexType>
 * 
 * </pre>
 *
 * @author fabian.lobo
 */
@Entity
@JsonRootName(value = "usuario")
@JsonSerialize(include = Inclusion.NON_NULL)
@Table(name = "CSS_USUARIO")
@XmlSeeAlso({Entidad.class})
@XmlType(name = "usuario", namespace = USUARIO_NAMESPACE, propOrder = {})
public class Usuario extends Entidad implements Serializable {

	private static final long serialVersionUID = -2749447785861866743L;
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "ID_USUARIO")
	@XmlElement(name = "id", namespace = USUARIO_NAMESPACE, required = true)
	private Long id;
	
	@ManyToOne(optional = false, fetch = FetchType.EAGER)
	@JoinColumn(name = "ID_ROL", nullable = false)
	@ForeignKey(name = "FK_USUARIO_ROL")
	@XmlElement(name = "rol", namespace = USUARIO_NAMESPACE, required = true)
	private Rol rol;
	
	@Column(name = "APELLIDO", length = 25, nullable = false)
	@XmlElement(name = "apellido", namespace = USUARIO_NAMESPACE, required = true)
	private String apellido;
	
	@Column(name = "PRIMER_NOMBRE", length = 25, nullable = false)
	@XmlElement(name = "primerNombre", namespace = USUARIO_NAMESPACE, required = true)
	private String primerNombre;

	@Column(name = "SEGUNDO_NOMBRE", length = 25, nullable = true)
	@XmlElement(name = "segundoNombre", namespace = USUARIO_NAMESPACE, required = false)
	private String segundoNombre;	
	
	@Column(name = "USUARIO", length = 50, nullable = false)
	@Index(name = "IDX_USUARIO")
	@XmlElement(name = "usuario", namespace = USUARIO_NAMESPACE, required = true)
	private String usuario;
	
	@Column(name = "PASSWORD", length = 80, nullable = false)
	@JsonIgnore
	@XmlTransient
	private String password;
	
	@Column(name = "ULTIMO_LOGIN", nullable = true)
	@JsonIgnore
	@Temporal(TemporalType.TIMESTAMP)
	@XmlElement(name = "ultimoLogin", namespace = USUARIO_NAMESPACE, required = false)
	private Date ultimoLogin;
	
	@Column(name = "INTENTOS", nullable = false)
	@XmlElement(name = "intentos", namespace = USUARIO_NAMESPACE, required = false)
	private int intentos = 0;
	
	@Column(name = "ULTIMO_INTENTO_FALLIDO", nullable = true)
	@JsonIgnore
	@Temporal(TemporalType.TIMESTAMP)
	@XmlElement(name = "ultimoIntentoFallido", namespace = USUARIO_NAMESPACE, required = false)
	private Date ultimoIntentoFallido;
	
	@Column(name = "ELIMINADO")
	@JsonIgnore
	@XmlTransient
	private boolean eliminado;
	
	@Column(name = "BLOQUEADO")
	@XmlElement(name = "bloqueado", namespace = USUARIO_NAMESPACE, required = true)
	private boolean bloqueado;
	
	@Column(name = "CORREO_ELECTRONICO", length = 50, nullable = true)
	@XmlElement(name = "correoElectronico", namespace = USUARIO_NAMESPACE, required = false)
	private String correoElectronico;
	
	@Column(name = "TELEFONO", length = 25, nullable = true)
	@XmlTransient
	private String telefono;
	
	@Column(name = "CELULAR", length = 25, nullable = true)
	@XmlTransient
	private String celular;
	
	@Column(name = "COMENTARIOS", length = 500, nullable = true)
	@XmlElement(name = "comentarios", namespace = USUARIO_NAMESPACE, required = false)
	private String comentarios;
	
	/*
	 *  GETTERS
	 */
	public Long getId() {
		return id;
	}
	public Rol getRol() {
		return rol;
	}
	public String getApellido() {
		return apellido;
	}
	public String getPrimerNombre() {
		return primerNombre;
	}
	public String getSegundoNombre() {
		return segundoNombre;
	}
	public String getUsuario() {
		return usuario;
	}
	public String getPassword() {
		return password;
	}
	public Date getUltimoLogin() {
		return ultimoLogin;
	}
	public int getIntentos() {
		return intentos;
	}
	public Date getUltimoIntentoFallido() {
		return ultimoIntentoFallido;
	}
	public boolean isEliminado() {
		return eliminado;
	}
	public boolean isBloqueado() {
		return bloqueado;
	}
	public String getCorreoElectronico() {
		return correoElectronico;
	}
	public String getTelefono() {
		return telefono;
	}
	public String getCelular() {
		return celular;
	}
	public String getComentarios() {
		return comentarios;
	}

	/*
	 *  SETTERS
	 */
	public void setId(Long id) {
		this.id = id;
	}
	public void setRol(Rol rol) {
		this.rol = rol;
	}
	public void setApellido(String apellido) {
		this.apellido = apellido;
	}
	public void setPrimerNombre(String primerNombre) {
		this.primerNombre = primerNombre;
	}
	public void setSegundoNombre(String segundoNombre) {
		this.segundoNombre = segundoNombre;
	}
	public void setUsuario(String usuario) {
		this.usuario = usuario;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public void setUltimoLogin(Date ultimoLogin) {
		this.ultimoLogin = ultimoLogin;
	}
	public void setIntentos(int intentos) {
		this.intentos = intentos;
	}
	public void setUltimoIntentoFallido(Date ultimoIntentoFallido) {
		this.ultimoIntentoFallido = ultimoIntentoFallido;
	}
	public void setEliminado(boolean eliminado) {
		this.eliminado = eliminado;
	}
	public void setBloqueado(boolean bloqueado) {
		this.bloqueado = bloqueado;
	}
	public void setCorreoElectronico(String correoElectronico) {
		this.correoElectronico = correoElectronico;
	}
	public void setTelefono(String telefono) {
		this.telefono = telefono;
	}
	public void setCelular(String celular) {
		this.celular = celular;
	}
	public void setComentarios(String comentarios) {
		this.comentarios = comentarios;
	}
	
	/*
	 *  CONVERSORES
	 */
	/**
	 * Convierte el número de teléfono fijo a texto
	 * 
	 * @author fabian.lobo
	 * @see HelperTextos#formatearTelefono(String, Locale)
	 */
	@JsonProperty(value = "telefono")
	//@XmlElement(name = "telefono", namespace = USUARIO_NAMESPACE, required = false)
	public String convierteTelefono() {
		return formatearTelefono(getTelefono(), URUGUAY);
	}
	
	/**
	 * Convierte el número de teléfono celular a texto
	 * 
	 * @author fabian.lobo
	 * @see HelperTextos#formatearTelefono(String, Locale)
	 */
	@JsonProperty(value = "celular")
	//@XmlElement(name = "celular", namespace = USUARIO_NAMESPACE, required = false)
	public String convierteCelular() {
		return formatearTelefono(getCelular(), URUGUAY);
	}
	
	/**
	 * Convierte la fecha del ultimo intento fallido a texto
	 * 
	 * @author fabian.lobo
	 * @see HelperFechaHora#fechaLarga()
	 */
	@JsonProperty(value = "ultimoIntentoFallido")
	public String convierteUltimoIntentoFallido() {
		return fechaLarga(getUltimoIntentoFallido());
	}
	
	/**
	 * Convierte la fecha del ultimo login a texto
	 * 
	 * @author fabian.lobo
	 * @see HelperFechaHora#fechaLarga()
	 */
	@JsonProperty(value = "ultimoLogin")
	public String convierteUltimoLogin() {
		return fechaLarga(getUltimoLogin());
	}
	
	/**
	 * Convierte los datos de primer nombre + segundo nombre + apellido a un nombre de una sola línea
	 * 
	 * @author fabian.lobo
	 * @see #getPrimerNombre()
	 * @see #getSegundoNombre()
	 * @see #getApellido()
	 */
	public String convierteNombreCompleto() {
		StringBuffer buffer = new StringBuffer();
		buffer.append(trimWhitespace(getPrimerNombre()));
		if(hasText(getSegundoNombre())) {
			buffer.append(" ");
			buffer.append(trimWhitespace(getSegundoNombre()));
		}
		buffer.append(" ");
		buffer.append(trimWhitespace(getApellido()));
		return trimWhitespace(buffer.toString());
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		Usuario otro = (Usuario) obj;
		if (id != null && otro.id != null && id.equals(otro.id)) {
			return true;
		}
		return false;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("Usuario [");
		builder.append("id=" + getId() + ", ");
		builder.append("apellido=" + getApellido() + ", ");
		builder.append("primerNombre=" + getPrimerNombre() + ", ");
		builder.append("segundoNombre=" + getSegundoNombre() + ", ");
		builder.append("usuario=" + getUsuario() + ", ");
		builder.append("password=" + getPassword() + ", ");
		builder.append("ultimoLogin=" + convierteUltimoLogin() + ", ");
		builder.append("intentos=" + getIntentos() + ", ");
		builder.append("ultimoIntentoFallido=" + convierteUltimoIntentoFallido() + ", ");
		builder.append("eliminado=" + isEliminado() + ", ");
		builder.append("bloqueado=" + isBloqueado() + ", ");
		builder.append("correoElectronico=" + getCorreoElectronico() + ", ");
		builder.append("telefono=" + convierteTelefono() + ", ");
		builder.append("celular=" + convierteCelular() + ", ");
		builder.append("comentarios=" + getComentarios() + ", ");
		builder.append("rol=[" + getRol().toString() + "]");
		builder.append("]");
		return builder.toString();
	}

	@Override
	public String toString(boolean incluirColecciones) {
		StringBuilder result = new StringBuilder(toString());
		
		if(incluirColecciones) {
			//int offset = result.length() - 2;
			//StringBuilder builder = new StringBuilder(", ");
			//builder.append("permissions=" + getPermissions());
			//result.insert(offset, builder.toString());
		}	
				
		return result.toString();
	}
}